﻿namespace EasyWeb
{
    using System;
    using System.Collections.Generic;

    public class EWStateBag
    {
        private readonly Dictionary<string, object> states = new Dictionary<string, object>();
        private List<string> changes;
        private bool isChangeTracking;

        public IReadOnlyList<string> Changes
        {
            get
            {
                EnsureChangesCollection();
                return this.changes;
            }
        }

        public bool HasChange
        {
            get { return this.changes != null && this.changes.Count > 0; }
        }

        public void EnableChangeTracking()
        {
            this.isChangeTracking = true;
        }

        public void DisableChangeTracking()
        {
            this.isChangeTracking = false;
        }

        public T Get<T>(string name)
        {
            return Get<T>(name, null);
        }

        public T Get<T>(string name, Func<T> getDefault)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");

            var result = default(T);

            object temp;

            if (this.states.TryGetValue(name, out temp))
            {
                result = (T)temp;
            }
            else if (getDefault != null)
            {
                result = getDefault();

                if (!object.Equals(result, default(T)))
                {
                    this.states[name] = result;
                }
            }

            return result;
        }

        public void Set<T>(string name, T value)
        {
            EWGuard.IsNotNullOrEmpty(name, "name");

            if (object.Equals(value, default(T)))
            {
                this.states.Remove(name);
                AddChange(name);
            }
            else
            {
                var oldValue = Get<T>(name, null);

                if (!object.Equals(oldValue, value))
                {
                    this.states[name] = value;
                    AddChange(name);
                }
            }
        }

        private void AddChange(string name)
        {
            if (this.isChangeTracking)
            {
                EnsureChangesCollection();
                this.changes.Add(name);
            }
        }

        private void EnsureChangesCollection()
        {
            if (this.changes == null)
            {
                this.changes = new List<string>();
            }
        }
    }
}
